package com.zhuyi.common.utils.uuid;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * ID生成器工具类
 *
 * @author zhuyi
 */
public class IdUtils {
    // 雪花算法生成器实例
    private static final SnowflakeIdWorker ID_WORKER = new SnowflakeIdWorker(1, 1);

    /**
     * 获取随机UUID
     *
     * @return 随机UUID
     */
    public static String randomUUID() {
        return java.util.UUID.randomUUID().toString();
    }

    /**
     * 简化的UUID，去掉了横线
     *
     * @return 简化的UUID，去掉了横线
     */
    public static String simpleUUID() {
        return java.util.UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 获取随机UUID，使用性能更好的ThreadLocalRandom生成UUID
     *
     * @return 随机UUID
     */
    public static String fastUUID() {
        return java.util.UUID.randomUUID().toString();
    }

    /**
     * 简化的UUID，去掉了横线，使用性能更好的ThreadLocalRandom生成UUID
     *
     * @return 简化的UUID，去掉了横线
     */
    public static String fastSimpleUUID() {
        return java.util.UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 获取雪花ID
     *
     * @return 雪花ID
     */
    public static long snowflakeId() {
        return ID_WORKER.nextId();
    }

    /**
     * 雪花ID生成器
     */
    private static class SnowflakeIdWorker {
        // 开始时间戳（从某个固定时间点开始生成）
        private final long twepoch = 1288834974657L;

        // 机器ID位数
        private final long workerIdBits = 5L;

        // 数据中心ID位数
        private final long datacenterIdBits = 5L;

        // 序列号位数
        private final long sequenceBits = 12L;

        // 最大机器ID
        private final long maxWorkerId = ~(-1L << workerIdBits);

        // 最大数据中心ID
        private final long maxDatacenterId = ~(-1L << datacenterIdBits);

        // 机器ID偏移量
        private final long workerIdShift = sequenceBits;

        // 数据中心ID偏移量
        private final long datacenterIdShift = sequenceBits + workerIdBits;

        // 时间戳偏移量
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

        // 序列号掩码
        private final long sequenceMask = ~(-1L << sequenceBits);

        private long workerId;
        private long datacenterId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;

        /**
         * 构造函数
         *
         * @param workerId     工作ID
         * @param datacenterId 数据中心ID
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenterId can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        /**
         * 生成下一个ID（线程安全）
         *
         * @return 雪花ID
         */
        public synchronized long nextId() {
            long timestamp = System.currentTimeMillis();

            if (timestamp < lastTimestamp) {
                throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
            }

            if (timestamp == lastTimestamp) {
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0L;
            }

            lastTimestamp = timestamp;

            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }

        /**
         * 等待直到下一毫秒
         *
         * @param lastTimestamp 上次生成ID的时间戳
         * @return 当前时间戳
         */
        private long tilNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }
    }
}
